package com.aibaixun.common.filter;

import com.aibaixun.common.constant.Constants;
import com.aibaixun.common.utils.HttpUtils;
import com.aibaixun.common.utils.SignUtils;
import com.aibaixun.common.utils.StringUtils;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedMap;

@Component
@Slf4j
public class SignAuthFilter extends OncePerRequestFilter {
    /**
     * OncePerRequestFilter过滤器保证一次请求只调用一次doFilterInternal方法;如内部的forward不会再多执行一次
     * @param request
     * @param response
     * @param filterChain
     * @throws ServletException
     * @throws IOException
     * */
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
        throws ServletException, IOException {

        //只拦截第三方请求的API
        if (request.getRequestURI().indexOf("openApi") < 0) {
            filterChain.doFilter(request, response);
            return;
        }

        //包装HttpServletRequest对象，缓存body数据，再次读取的时候将缓存的值写出,解决HttpServetRequest读取body只能一次的问题
        HttpServletRequest requestWrapper = null;
        if (request instanceof HttpServletRequest) {
            requestWrapper = new BodyReaderHttpServletRequestWrapper(request);
        }

        //校验头部参数是否有效
        boolean isValid = SignUtils.verifyHeaderParams(requestWrapper);
        if (isValid) {
            //获取全部参数(包括URL和Body上的)
            SortedMap<String, String> allParams = HttpUtils.getAllParams(requestWrapper);
            /**
             * ppSecret需要自己业务去获取，它的作用主要是区分不同客户端app。
             * 并且利用获取到的appSecret参与到sign签名，保证了客户端的请求签名是由我们后台控制的，
             * 我们可以为不同的客户端颁发不同的appSecret。
             * */
            //根据调用传递的appId获取对应的appSecret（应用密钥）
            String appSecret = getAppSecret(allParams.get(Constants.APP_ID));
            //appSecret（应用密钥）存在
            if (StringUtils.isNotEmpty(appSecret)) {
                //将调用方应用id对应的应用密钥与请求参数合成指定
                boolean isSigned = SignUtils.verifySignature(allParams, appSecret);
                if (isSigned) {
                    log.info("签名通过");
                    filterChain.doFilter(request, response);
                    return;
                }
            }
        }
        log.info("签名参数校验出错");
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json;charset=utf-8");
        PrintWriter out = response.getWriter();
        JSONObject resParam = new JSONObject();
        resParam.put("msg", "签名参数校验出错");
        resParam.put("code", 401);
        out.append(resParam.toJSONString());
    }

    /**
     *  获取appId对应的secret
     *  @param appId 应用id
     *  @return
     *  */
    public String getAppSecret(String appId) {
        Map<String, String> map = new HashMap<>();
        map.put("gs001", "asd123fhg3b7fgh7dfg");
        map.put("gs002", "hghfgh123btgfyh1212");
        return map.get(appId);
    }
}
